问题
不可变类是其实例不能被修改的类,没有实例中所包含的数据域,在实例被创建的时候被初始化,且在实例的生命周期中不能被修改。JAVA中有许多不可变类,如String,值的基本包装类型,BigInteger和BigDecimal等,不可变类是线程安全的。不可变有很多优点,那么设计不可变类的原则有哪些?
解决
设计不可变类有以下几条规则:
- 不要提供任何修改实例数据域的setter方法;
- 保证类不会被扩展:防止子类恶意修改实例对象,应该禁止类被子类扩展,可以将其定义为final;或者让类所有的构造器都变成私有的或者包级私有的,并添加公有的静态工厂来代替公有的构造器;
- 所有的域都是final的;
- 所有的域都成为私有的,这样可以防止客户端获得访问被域引用的可变对象的权限,并防止客户端修改这些对象;
- 确保对于任何可变组件的互斥性访问:如果类具有指向可变对象的域,则必须确保客户端无法获得指向这些对象的引用;
示例
例如,String不可变类的具体实现为:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** The offset is the first index of the storage that is used. */ private final int offset; /** The count is the number of characters in the String. */ private final int count; /** Cache the hash code for the string */ private int hash; // Default to 0 .... public String(char value[]) { this.value = Arrays.copyOf(value, value.length); } ... public char[] toCharArray() { // Cannot use Arrays.copyOf because of class initialization order issues char result[] = new char[value.length]; System.arraycopy(value, 0, result, 0, value.length); return result; } ... }
如上代码所示,可以观察到以下设计细节:
- String类被final修饰,不可继承
- string内部所有成员都设置为私有变量
- 不存在value的setter
- 并将value和offset设置为final。
- 当传入可变数组value[]时,进行深拷贝而不是直接将value[]复制给内部变量.
- 获取value时不是直接返回对象引用,而是返回对象的copy.
结论
不可变类有很多好处,因此合适的适用场景下,可以考虑将类设计生不可变类,并遵守不可变类的设计原则。